package com.yifang.yibingproject.config;


import cn.hutool.json.JSONUtil;
import com.yifang.yibingproject.entity.SysLogEntity;
import com.yifang.yibingproject.service.SysLogService;
import com.yifang.yibingproject.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 日志切面
 */
@Component
@Aspect
@Slf4j
public class LogAspectConfig {
    private static final ThreadLocal<Long> currentTime = new ThreadLocal<>();

    @Autowired
    @Lazy
    private SysLogService sysLogService;

    /**
     * 日志切点
     */
    @Pointcut("@annotation(com.yifang.yibingproject.utils.Log)")
    public void logPointcut() {
        // 日志切点
    }

    /**
     * 配置环绕通知,使用在方法logPointcut()上注册的切入点
     *
     * @param joinPoint join point for advice
     */
    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        currentTime.set(System.currentTimeMillis());
        result = joinPoint.proceed();
        SysLogEntity logEntity = new SysLogEntity();
        logEntity.setId(CommonUtils.getUUID());
        logEntity.setTime((int) (System.currentTimeMillis() - currentTime.get()));
        currentTime.remove();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);
        logEntity.setDescription(logAnnotation.value());
        if (logAnnotation.resp()) {
            logEntity.setResp(JSONUtil.toJsonStr(result));
        }
        // 方法路径
        String methodName = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
        logEntity.setMethod(methodName);
        if (methodName.contains(".controller.")) {
            // 后端接口
            logEntity.setType(0);
        } else if (methodName.contains(".api.")) {
            // 移动端接口
            logEntity.setType(1);
        }else {
            // 默认都是后端接口
            logEntity.setType(0);
        }
        // 请求参数
        if (logAnnotation.params()) {
            String params = getParameter(method, joinPoint.getArgs());
            logEntity.setParams(params);
        }

        HttpServletRequest request = getHttpServletRequest();
        logEntity.setUserId(SystemContext.getCurrentUserId());
        // 请求IP地址
        logEntity.setRequestIp(IPUtils.getIpAddress(request));
        // 请求方法
        logEntity.setRequestMethod(request.getMethod());
        // 请求路径
        StringBuffer requestURL = request.getRequestURL();
        String queryString = request.getQueryString();
        if (queryString != null) {
            requestURL.append("?").append(queryString);
        }
        logEntity.setRequestUrl(requestURL.toString());
        // 异步保存日志
        sysLogService.add(logEntity);
        return result;
    }

    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
    }

    /**
     * 根据方法和传入的参数获取请求参数
     */
    private String getParameter(Method method, Object[] args) {
        List<Object> argList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            // 忽略文件上传参数
            if (args[i] instanceof MultipartFile) {
                continue;
            }

            // 将RequestBody注解修饰的参数作为请求参数
            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
            if (requestBody != null) {
                argList.add(args[i]);
                continue;
            }

            // 将ModelAttribute注解修饰的参数作为请求参数
            ModelAttribute modelAttribute = parameters[i].getAnnotation(ModelAttribute.class);
            if (modelAttribute != null) {
                argList.add(args[i]);
            }

            // 将RequestParam注解修饰的参数作为请求参数
            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
            if (requestParam != null) {
                Map<String, Object> map = new HashMap<>();
                String key = parameters[i].getName();
                if (StringUtilOwn.notEmpty(requestParam.value())) {
                    key = requestParam.value();
                }
                map.put(key, args[i]);
                argList.add(map);
            }
        }
        if (argList.size() == 0) {
            return "";
        }
        return argList.size() == 1 ? JSONUtil.toJsonStr(argList.get(0)) : JSONUtil.toJsonStr(argList);
    }
}
